﻿--[[
	Copyright (c) 2012 Eyal Shilony

	Permission is hereby granted, free of charge, to any person obtaining
	a copy of this software and associated documentation files (the
	"Software"), to deal in the Software without restriction, including
	without limitation the rights to use, copy, modify, merge, publish,
	distribute, sublicense, and/or sell copies of the Software, and to
	permit persons to whom the Software is furnished to do so, subject to
	the following conditions:

	The above copyright notice and this permission notice shall be
	included in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
	LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
	OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
	WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
	
	-- A library that provides API to access the game interfaces through predefined actions.
]]

assert(LibStub, "Commando-1.0 requires LibStub")

local lib, minor = LibStub:NewLibrary("Commando-1.0", 0)
if not lib then return end
minor = minor or 0

local GetUIPanel = GetUIPanel
local HideUIPanel = HideUIPanel
local ShowUIPanel = ShowUIPanel
local UIPanelWindows = UIPanelWindows

--[[ LOCALIZATION ]]

local BAGS = "Bags"
local CALENDAR = "Calendar"
local CHARACTER_MSG = "|cffFFFF00The action '%s' cannot run because the character level is too low.|r"
local COMBAT_MSG = "|cffFFFF00The action '%s' cannot run in combat due to Blizzard in-combat restrictions.|r"

--[[ MANAGED FRAMES ]]

-- Restricts the following frames from opening or closing in-combat through this library.
RestrictedFrames = {
	--["PaperDollFrame"] = true,
	--["CharacterFrame"] = true,
	["SpellBookFrame"] = true,
}

-- Hides the GameMenuFrame for the following frames. 
GameMenu_OptionFrames = {
	["VideoOptionsFrame"] = true,
	["AudioOptionsFrame"] = true,
	["InterfaceOptionsFrame"] = true,
	["MacOptionsFrame"] = true,
	["KeyBindingFrame"] = true,
}

-- Must be loaded before the action runs.
LoD_AddonFrames = {
	["PlayerTalentFrame"] = TalentFrame_LoadUI,
	["AchievementFrame"] = AchievementFrame_LoadUI,
	["CalendarFrame"] = Calendar_LoadUI,
	["KeyBindingFrame"] = KeyBindingFrame_LoadUI,
	["MacroFrame"] = MacroFrame_LoadUI,
	["TimeManagerFrame"] = TimeManager_LoadUI,
	["GuildFrame"] = GuildFrame_LoadUI,
	["EncounterJournal"] = EncounterJournal_LoadUI(),
}

--[[ PREDEFINED ACTIONS ]]

--[[
	name - (string) The name of the action.
	title - (string) The title of the action.
	shorthand - (string) An optional shorthand for the action.
	frame - (string) The associated frame of the executing action.
	level - (number) The minimum level of the player required to run the action.
	func - (function) The function (handler) to execute.
]]
local actions = {
	{
		name = "character",
		title = CHARACTER_BUTTON,
		shorthand = "char",
		frame = "CharacterFrame",
		func = function() ToggleCharacter("PaperDollFrame") end,
	},
	{
		name = "spellbook",
		title = SPELLBOOK_ABILITIES_BUTTON,
		shorthand = "sb",
		frame = "SpellBookFrame",
		func = function() ToggleFrame(SpellBookFrame) end,
	},
	{
		name = "talents",
		title = TALENTS_BUTTON,
		shorthand = "tal",
		frame = "PlayerTalentFrame",
		level = SHOW_TALENT_LEVEL,
		func = function() ToggleTalentFrame() end,
	},
	{
		name = "achievements",
		title = ACHIEVEMENT_BUTTON,
		shorthand = "achi",
		frame = "AchievementFrame",
		func = function() ToggleAchievementFrame() end,
	},
	{
		name = "calendar",
		title = CALENDAR,
		shorthand = "cal",
		frame = "CalendarFrame",
		func = function() ToggleCalendar() end,
	},
	{
		name = "questlog",
		title = QUESTLOG_BUTTON,
		shorthand = "ql",
		frame = "QuestLogFrame",
		func = function() ToggleFrame(QuestLogFrame) end,
	},
	{
		name = "social",
		title = SOCIAL_BUTTON,
		shorthand = "soc",
		frame = "FriendsFrame",
		func = function() ToggleFriendsFrame() end,
	},
	{
		name = "pvp",
		title = PLAYER_V_PLAYER,
		frame = "PVPFrame",
		level = SHOW_PVP_LEVEL,
		func = function() TogglePVPFrame() end,
	},
	{
		name = "lfd",
		title = DUNGEONS_BUTTON,
		frame = "LFDParentFrame",
		level = SHOW_LFD_LEVEL,
		func = function() ToggleLFDParentFrame() end,
	},
	{
		name = "lfr",
		title = RAID_FINDER,
		frame = "LFRParentFrame",
		level = SHOW_LFD_LEVEL,
		func = function() ToggleRaidFrame() end,
	},
	{
		name = "companions",
		title = MOUNTS_AND_PETS,
		shorthand = "pets",
		frame = "PetJournalParent",
		func = function() TogglePetJournal() end,
	},
	{
		name = "journal",
		title = ENCOUNTER_JOURNAL,
		frame = "EncounterJournal",
		level = SHOW_LFD_LEVEL,
		func = function() ToggleEncounterJournal() end,
	},
	{
		name = "guild",
		title = GUILD,
		frame = "GuildFrame",
		func = function() ToggleGuildFrame() end,
	},
	{
		name = "backpack",
		title = BACKPACK_TOOLTIP,
		func = function() 
			if IsModifierKeyDown() then
				ToggleAllBags()
			else
				ToggleBackpack()
			end
		end,
	},
	{
		name = "bags",
		title = BAGS,
		func = function() 
			ToggleAllBags()
		end,
	},
	{
		name = "keyring",
		title = KEYRING,
		func = function() 
			ToggleKeyRing()
		end,
	},
	{
		name = "time",
		title = TIMEMANAGER_TITLE,
		frame = "TimeManagerFrame",
		func = function() ToggleTimeManager() end,
	},
	{
		name = "help",
		title = HELP_BUTTON,
		frame = "HelpFrame",
		func = function() ToggleHelpFrame() end,
	},
	{
		name = "options",
		title = SYSTEMOPTIONS_MENU,
		shorthand = "op",
		frame = "VideoOptionsFrame",
		func = function()
			if not VideoOptionsFrame:IsVisible() then
				ShowUIPanel(VideoOptionsFrame)
				VideoOptionsFrame.lastFrame = GameMenuFrame
				VideoOptionsFrame.ignoreMenu = true
			else
				HideUIPanel(VideoOptionsFrame)
			end
		end,
	},
	{
		name = "interface",
		title = UIOPTIONS_MENU,
		shorthand = "ui",
		frame = "InterfaceOptionsFrame",
		func = function()
			if not InterfaceOptionsFrame:IsVisible() then
				ShowUIPanel(InterfaceOptionsFrame)
				InterfaceOptionsFrame.lastFrame = GameMenuFrame
				InterfaceOptionsFrame.ignoreMenu = true
			else
				HideUIPanel(InterfaceOptionsFrame)
			end
		end,
	},
	{
		name = "mac",
		title = MAC_OPTIONS,
		frame = "MacOptionsFrame",
		func = function()
			if not MacOptionsFrame:IsVisible() then
				ShowUIPanel(MacOptionsFrame)
				MacOptionsFrame.ignoreMenu = true
			else
				HideUIPanel(MacOptionsFrame)
			end
		end,
	},
	{
		name = "bindings",
		title = KEY_BINDINGS,
		shorthand = "binds",
		frame = "KeyBindingFrame",
		func = function()
			if not KeyBindingFrame:IsVisible() then
				ShowUIPanel(KeyBindingFrame)
				KeyBindingFrame.ignoreMenu = true
			else
				HideUIPanel(KeyBindingFrame)
			end
		end,
	},
	{
		name = "macro",
		title = MACROS,
		frame = "MacroFrame",
		func = function()
			if not MacroFrame:IsVisible() then
				ShowUIPanel(MacroFrame)
			else
				HideUIPanel(MacroFrame)
			end
		end,
	}
}

-- if we're not using a mac client, remove the action.
if IsMacClient() ~= 1 then
	for i, v in ipairs(actions) do
		if v.name == "mac" then
			table.remove(actions, i) 
			break
		end
	end
end

local function comp(a, b)
	return a.title:lower() < b.title:lower()
end

table.sort(actions, comp)

--[[ APIs ]]

--[[ GetAction(name) - Gets the action by given key/shorthand or title

	Arguments.
	name - (string) The key/shorthand or title of the action to retrieve.
	
	Returns.
	action - (table) The action.
	index - (number) The index of action.
	
]]
function lib:GetAction(name)
	local action, index  = nil, 0
	if  name and type(name) == "string" then
		for i, v in ipairs(actions) do
			if v.name == name or v.shorthand == name or v.title == name then
				action = v
				index = i
				break
			end
		end
	end
	return action, index
end

--[[ GetActionByIndex(index) - Gets the action by given index

	Arguments.
	index - (number) The index of the action to retrieve.
	
	Returns.
	action - (table) The action.
	
]]
function lib:GetActionByIndex(index)
	local action
	if  index and type(index) == "number" then
		action = actions[index]
	end
	return action
end

--[[ Actions() - Iterates over the actions. 

	Returns.
	iterator - (function) An iterator to traverse over the actions.
	
]]
function lib:Actions()
	return ipairs(actions)
end

--[[ RunAction(name) - Runs the action.

	Arguments.
	name - (string) The key/shorthand  or title of the action to run.
	
]]

local function hideUIPanel(self)
	if self.ignoreMenu then
		HideUIPanel(GameMenuFrame)
		self.ignoreMenu = nil
	end
end

function lib:RunAction(name)
	local action = self:GetAction(name)
	if action and action.func and type(action.func) == "function" then
		local playerLevel = UnitLevel("player");
		if action.level and tonumber(action.level) and playerLevel < action.level  then
			print(CHARACTER_MSG:format(action.title))
		elseif action.frame then
		
			local inCombat = InCombatLockdown()
			local isRestricted = RestrictedFrames[action.frame]
			
			if not isRestricted or not inCombat and isRestricted then
				-- Determines whether to load the associated addon 
				local loadLoD = LoD_AddonFrames[action.frame]
				
				if loadLoD then loadLoD() end
				
				-- Manage the interfaces
				local panel = UIPanelWindows[action.frame]
				local centerFrame = not (GetUIPanel("left") or GetUIPanel("right") or GetUIPanel("doublewide")) and GetUIPanel("center")
				
				if panel and panel.area ~= "center" and centerFrame and centerFrame:IsShown() then
					HideUIPanel(centerFrame)
				end
				
				-- Run the action
				action.func()
				
				if GameMenu_OptionFrames[action.frame] then
					local frame = _G[action.frame]
					if frame then
						-- Hides the GameMenuFrame
						frame:HookScript("OnHide", hideUIPanel) 
					end
					GameMenu_OptionFrames[action.frame] = nil
				end
			else
				print(COMBAT_MSG:format(action.title))
			end
			
		else
			action.func()
		end
	end
end
